package br.edu.ufcg.greengrid.model;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.LinkedList;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import br.edu.ufcg.greengrid.AbstractTest;
import br.edu.ufcg.greengrid.event.JobSubmissionEvent;
import br.edu.ufcg.greengrid.event.MachineAvailabilityEvent;

public class SimulatorTest extends AbstractTest {

	@Before
	public void setUp() throws Exception {

	}

	@After
	public void tearDown() throws Exception {

	}

	@Test
	public void testRunEmptyScenario() {
		Grid grid = new Grid();
		List<MachineAvailabilityEvent> machineAvailabilityWorkload = new LinkedList<MachineAvailabilityEvent>();
		List<JobSubmissionEvent> gridWorkload = new LinkedList<JobSubmissionEvent>();

		try {
			new Simulator(grid, machineAvailabilityWorkload, gridWorkload);
			fail("Error! Shouldn't be possible to run a scenario with empty machine availability events!");
		} catch (IllegalArgumentException e) {
		}
	}

	@SuppressWarnings("serial")
	@Test
	public void testRunSimpleScenario() {

		Grid grid = new Grid();
		List<MachineAvailabilityEvent> machineAvailabilityWorkload = new LinkedList<MachineAvailabilityEvent>() {
			{
				add(new MachineAvailabilityEvent("m1", 1, 5));
				add(new MachineAvailabilityEvent("m2", 3, 10));
				add(new MachineAvailabilityEvent("m3", 4, 15));
			}
		};
		List<JobSubmissionEvent> gridWorkload = new LinkedList<JobSubmissionEvent>() {
			{
				add(new JobSubmissionEvent("id1", 1, 5));
				add(new JobSubmissionEvent("id2", 2, 10));
			}
		};

		//<simulation> <expectedClock>, <expectedNumJobsArrivedInLastTurn>, <expectedDone>
		Simulator s = new Simulator(grid, machineAvailabilityWorkload, gridWorkload);

		s.nextStep();
		assertE(s, 1, 1, false);

		s.nextStep();
		assertE(s, 2, 1, false);

		for (int i = 3; i < 12; i++) {
			s.nextStep();
			assertE(s, i, 0, false);
		}

		s.nextStep();
		assertE(s, 12, 0, true);
	}

	@SuppressWarnings("serial")
	@Test
	public void testJobSubmissionBeforeFirstIdleMachineIsAvailable(){
		
		Grid grid = new Grid();
		List<MachineAvailabilityEvent> machineAvailabilityWorkload = new LinkedList<MachineAvailabilityEvent>() {
			{
				add(new MachineAvailabilityEvent("m1", 2, 5));
			}
		};
		List<JobSubmissionEvent> gridWorkload = new LinkedList<JobSubmissionEvent>() {
			{
				add(new JobSubmissionEvent("id1", 1, 5));
			}
		};

		try {
			new Simulator(grid, machineAvailabilityWorkload, gridWorkload);
			fail("Error! Should not be allowed to submit job before the first idle machine appeared");
		} catch(IllegalArgumentException iae){
		}
		
	}
	
	@SuppressWarnings("serial")
	@Test
	public void testEndlessMachineAvailabilityEvents() {
		
		List<MachineAvailabilityEvent> machineAvailabilityWorkload = new LinkedList<MachineAvailabilityEvent>() {
			{
				add(new MachineAvailabilityEvent("m1", 6, 3));
				add(new MachineAvailabilityEvent("m2", 10, 2));
			}
		};
		
		List<MachineAvailabilityEvent> expectedMachineAvailabilityWorkloadGenerated = new LinkedList<MachineAvailabilityEvent>() {
			{
				add(new MachineAvailabilityEvent("m1", 6, 3));
				add(new MachineAvailabilityEvent("m2", 10, 2));
				
				add(new MachineAvailabilityEvent("m1", 11, 3));
				add(new MachineAvailabilityEvent("m2", 15, 2));
				
				add(new MachineAvailabilityEvent("m1", 16, 3));
				add(new MachineAvailabilityEvent("m2", 20, 2));
				
				add(new MachineAvailabilityEvent("m1", 21, 3));
				add(new MachineAvailabilityEvent("m2", 25, 2));
			}
		};
		List<MachineAvailabilityEvent> actualMachineAvailabilityWorkloadGenerated = new LinkedList<MachineAvailabilityEvent>();
		
		Grid g = new Grid();
		Simulator s = new Simulator(g, machineAvailabilityWorkload, new LinkedList<JobSubmissionEvent>());
		
		for (int i = 0; i < expectedMachineAvailabilityWorkloadGenerated.size(); i++) {
			assertTrue(s.hasNextMachineAvailabilityEvent());
			actualMachineAvailabilityWorkloadGenerated.add(s.getNextMachineAvailabilityEvent());
		}
		
		assertIsEqual(expectedMachineAvailabilityWorkloadGenerated, actualMachineAvailabilityWorkloadGenerated);
	}
	
	private void assertE(Simulator s, long expectedClock, int expectedNumJobsArrivedInLastTurn, boolean expectedDone) {
		assertEquals(expectedClock, s.getClock());
		assertEquals(expectedNumJobsArrivedInLastTurn, s.getNumJobsArrivedInLastTurn());
		assertEquals(expectedDone, s.done());
	}

}
